package cc.shacocloud.mirage.utils.reflection;

import cc.shacocloud.mirage.utils.ClassUtil;
import cc.shacocloud.mirage.utils.charSequence.StrUtil;
import cc.shacocloud.mirage.utils.collection.ArrayUtil;
import cc.shacocloud.mirage.utils.map.ConcurrentReferenceHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.lang.reflect.*;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 反射工具类
 *
 * @author 思追(shaco)
 */
public class ReflectUtil {
    
    /**
     * 方法缓存
     * <p>
     * 被覆盖的方法不包含其中
     */
    private static final Map<Class<?>, Method[]> METHODS_CACHE = new ConcurrentReferenceHashMap<>(128);
    
    /**
     * 方法缓存
     * <p>
     * 被覆盖的方法也包含其中
     */
    private static final Map<Class<?>, Method[]> METHODS_OVERRIDDEN_CACHE = new ConcurrentReferenceHashMap<>(128);
    
    /**
     * 字段缓存
     */
    private static final Map<Class<?>, Field[]> FIELDS_CACHE = new ConcurrentReferenceHashMap<>(128);
    
    /**
     * 构造函数对象缓存
     */
    private static final Map<Class<?>, Constructor<?>[]> CONSTRUCTORS_CACHE = new ConcurrentReferenceHashMap<>(128);
    
    private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
    
    /**
     * 查找类中的指定参数的构造方法
     *
     * @param <T>            对象类型
     * @param clazz          类
     * @param parameterTypes 参数类型，只要任何一个参数是指定参数的父类或接口或相等即可，此参数可以不传
     * @return 构造方法，如果未找到返回null
     */
    @Nullable
    @SuppressWarnings("unchecked")
    public static <T> Constructor<T> getConstructor(@NotNull Class<T> clazz, Class<?>... parameterTypes) {
        Constructor<?>[] constructors = getConstructors(clazz);
        
        Class<?>[] pts;
        for (Constructor<?> constructor : constructors) {
            pts = constructor.getParameterTypes();
            if (ClassUtil.isAllAssignableFrom(pts, parameterTypes)) {
                return (Constructor<T>) constructor;
            }
        }
        
        return null;
    }
    
    /**
     * 获得一个类中所有构造列表
     *
     * @param <T>       构造的对象类型
     * @param beanClass 类
     * @return 字段列表
     */
    @SuppressWarnings("unchecked")
    public static <T> Constructor<T>[] getConstructors(@NotNull Class<T> beanClass) {
        return (Constructor<T>[]) CONSTRUCTORS_CACHE.computeIfAbsent(beanClass, Class::getDeclaredConstructors);
    }
    
    /**
     * 获取字段值
     *
     * @param obj       对象，如果static字段，此处为类
     * @param fieldName 字段名
     * @return 字段值
     */
    public static Object getFieldValue(Object obj, String fieldName) {
        if (null == obj || StrUtil.isBlank(fieldName)) {
            return null;
        }
        return getFieldValue(obj, getField(obj instanceof Class ? (Class<?>) obj : obj.getClass(), fieldName));
    }
    
    /**
     * 获取字段值
     *
     * @param obj   对象，static字段则此字段为null
     * @param field 字段
     * @return 字段值
     */
    public static Object getFieldValue(Object obj, Field field) {
        if (null == field) {
            return null;
        }
        if (obj instanceof Class) {
            // 静态字段获取时对象为null
            obj = null;
        }
        
        setAccessible(obj, field);
        Object result;
        try {
            result = field.get(obj);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        return result;
    }
    
    /**
     * 设置字段值
     *
     * @param obj   对象，如果是static字段，此参数为null
     * @param field 字段
     * @param value 值
     */
    public static void setFieldValue(@Nullable Object obj, @NotNull Field field, Object value) {
        setAccessible(obj, field);
        try {
            field.set(obj instanceof Class ? null : obj, value);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(
                    String.format("给对象 %s 的属性 %s 赋值发生例外！", Objects.nonNull(obj) ? obj.getClass() : field.getDeclaringClass(), field.getName()),
                    e
            );
        }
    }
    
    /**
     * 设置为可访问（私有可以被外部调用）
     *
     * @param <T>              AccessibleObject的子类，比如Class、Method、Field等
     * @param accessibleObject 可设置访问权限的对象，比如Class、Method、Field等
     * @return 被设置可访问的对象
     */
    public static <T extends AccessibleObject> T setAccessible(@Nullable Object obj, T accessibleObject) {
        if (Objects.isNull(obj) && accessibleObject instanceof Executable) {
            makeAccessible((Executable) accessibleObject);
        } else {
            if (Objects.nonNull(accessibleObject) && Objects.nonNull(obj) && !accessibleObject.canAccess(obj)) {
                
                AccessController.doPrivileged((PrivilegedAction<T>) () -> {
                    accessibleObject.setAccessible(true);
                    return null;
                });
                
            }
        }
        return accessibleObject;
    }
    
    /**
     * 使给定的构造可访问，并在必要时显式设置其可访问性。
     *
     * @see #setAccessible
     */
    @SuppressWarnings("deprecation")  // on JDK 9
    public static void makeAccessible(@NotNull Executable executable) {
        if ((!Modifier.isPublic(executable.getModifiers()) ||
                !Modifier.isPublic(executable.getDeclaringClass().getModifiers())) && !executable.isAccessible()) {
            
            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                executable.setAccessible(true);
                return null;
            });
            
        }
    }
    
    
    /**
     * 获取对象中所有的成员，即 {@link Method} 和 {@link Field}
     */
    @NotNull
    public static Member @NotNull [] getMembers(@NotNull Class<?> clazz) {
        Field[] fields = getFields(clazz);
        Method[] methods = getMethods(clazz);
        
        Member[] members = new Member[fields.length + methods.length];
        
        int t = 0;
        for (Field field : fields) {
            members[t] = field;
            t++;
        }
        
        for (Method method : methods) {
            members[t] = method;
            t++;
        }
        
        return members;
    }
    
    /**
     * 查找指定类中的指定name的字段（包括非public字段），也包括父类和Object类的字段， 字段不存在则返回{@code null}
     *
     * @param beanClass 被查找字段的类,不能为null
     * @param name      字段名
     * @return 字段
     * @throws SecurityException 安全异常
     */
    @Nullable
    public static Field getField(Class<?> beanClass, String name) throws SecurityException {
        final Field[] fields = getFields(beanClass);
        return Arrays.stream(fields).filter((field) -> name.equals(field.getName())).findFirst().orElse(null);
    }
    
    /**
     * 获得一个类中所有字段列表，包括其父类中的字段<br>
     * 如果子类与父类中存在同名字段，则这两个字段同时存在，子类字段在前，父类字段在后。
     *
     * @param beanClass 类
     * @return 字段列表
     * @throws SecurityException 安全检查异常
     */
    public static Field[] getFields(@NotNull Class<?> beanClass) throws SecurityException {
        return FIELDS_CACHE.computeIfAbsent(beanClass, (k) -> getFieldsDirectly(k, true));
    }
    
    /**
     * 获得一个类中所有字段列表，直接反射获取，无缓存<br>
     * 如果子类与父类中存在同名字段，则这两个字段同时存在，子类字段在前，父类字段在后。
     *
     * @param beanClass            类
     * @param withSuperClassFields 是否包括父类的字段列表
     * @return 字段列表
     * @throws SecurityException 安全检查异常
     */
    public static Field @NotNull [] getFieldsDirectly(@NotNull Class<?> beanClass, boolean withSuperClassFields) throws SecurityException {
        List<Field> allFields = new ArrayList<>();
        Class<?> searchType = beanClass;
        Field[] declaredFields;
        while (searchType != null) {
            declaredFields = searchType.getDeclaredFields();
            allFields.addAll(Arrays.asList(declaredFields));
            searchType = withSuperClassFields ? searchType.getSuperclass() : null;
        }
        
        return allFields.toArray(new Field[0]);
    }
    
    /**
     * 获得一个类中所有方法列表，包括其父类中的方法。
     * 使用 {@link #getUniqueKey(Method)} 来对方法进行去重，即：被覆盖的方法不包含其中
     *
     * @param beanClass 类，非{@code null}
     * @return 方法列表
     * @throws SecurityException 安全检查异常
     */
    public static Method[] getMethods(@NotNull Class<?> beanClass) throws SecurityException {
        return METHODS_CACHE.computeIfAbsent(beanClass, k -> getMethodsDirectly(k, true, true));
    }
    
    /**
     * 获得一个类中所有方法列表，包括其父类中的方法，包含被覆盖的方法
     *
     * @param beanClass 类，非{@code null}
     * @return 方法列表
     * @throws SecurityException 安全检查异常
     */
    public static Method[] getMethodsIncludeOverridden(@NotNull Class<?> beanClass) throws SecurityException {
        return METHODS_OVERRIDDEN_CACHE.computeIfAbsent(beanClass, k -> {
            Function<Method, String> uniqueGenerator = ReflectUtil::getMethodDescription;
            return getMethodsDirectly(k, true, true, uniqueGenerator);
        });
    }
    
    /**
     * 获得指定类过滤后的Public方法列表
     *
     * @param clazz  查找方法的类
     * @param filter 过滤器
     * @return 过滤后的方法列表
     * @throws SecurityException 安全异常
     */
    public static Method[] getMethods(Class<?> clazz, Predicate<Method> filter) throws SecurityException {
        if (null == clazz) {
            return null;
        }
        return ArrayUtil.filter(getMethods(clazz), filter);
    }
    
    /**
     * 获得一个类中所有方法列表，直接反射获取，无缓存<br>
     * 接口获取方法和默认方法，获取的方法包括：
     * <ul>
     *     <li>本类中的所有方法（包括static方法）</li>
     *     <li>父类中的所有方法（包括static方法）</li>
     *     <li>Object中（包括static方法）</li>
     * </ul>
     *
     * @param beanClass            类或接口
     * @param withSupers           是否包括父类或接口的方法列表
     * @param withMethodFromObject 是否包括Object中的方法
     * @return 方法列表
     * @throws SecurityException 安全检查异常
     */
    @NotNull
    public static Method[] getMethodsDirectly(@NotNull Class<?> beanClass,
                                              boolean withSupers,
                                              boolean withMethodFromObject) throws SecurityException {
        Function<Method, String> uniqueGenerator = ReflectUtil::getUniqueKey;
        return getMethodsDirectly(beanClass, withSupers, withMethodFromObject, uniqueGenerator);
    }
    
    /**
     * 获得一个类中所有方法列表，直接反射获取，无缓存<br>
     * 接口获取方法和默认方法，获取的方法包括：
     * <ul>
     *     <li>本类中的所有方法（包括static方法）</li>
     *     <li>父类中的所有方法（包括static方法）</li>
     *     <li>Object中（包括static方法）</li>
     * </ul>
     *
     * @param beanClass            类或接口
     * @param withSupers           是否包括父类或接口的方法列表
     * @param withMethodFromObject 是否包括Object中的方法
     * @param uniqueGenerator      生成方法的唯一键来进行去重
     * @return 方法列表
     * @throws SecurityException 安全检查异常
     */
    @NotNull
    public static Method[] getMethodsDirectly(@NotNull Class<?> beanClass,
                                              boolean withSupers,
                                              boolean withMethodFromObject,
                                              @NotNull Function<Method, String> uniqueGenerator) throws SecurityException {
        if (beanClass.isInterface()) {
            // 对于接口，直接调用Class.getMethods方法获取所有方法，因为接口都是public方法
            return withSupers ? beanClass.getMethods() : beanClass.getDeclaredMethods();
        }
        
        final Map<String, Method> result = new HashMap<>();
        Class<?> searchType = beanClass;
        while (searchType != null) {
            if (!withMethodFromObject && Object.class == searchType) {
                break;
            }
            
            for (Method declaredMethod : searchType.getDeclaredMethods()) {
                result.putIfAbsent(uniqueGenerator.apply(declaredMethod), declaredMethod);
            }
            
            for (Method method : getDefaultMethodsFromInterface(searchType)) {
                result.putIfAbsent(uniqueGenerator.apply(method), method);
            }
            
            searchType = (withSupers && !searchType.isInterface()) ? searchType.getSuperclass() : null;
        }
        
        return result.values().toArray(new Method[0]);
    }
    
    /**
     * 查找指定对象中的所有方法（包括非public方法），也包括父对象和Object类的方法
     *
     * <p>
     * 此方法为精准获取方法名，即方法名和参数数量和类型必须一致，否则返回{@code null}。
     * </p>
     *
     * @param obj        被查找的对象，如果为{@code null}返回{@code null}
     * @param methodName 方法名，如果为空字符串返回{@code null}
     * @param args       参数
     * @return 方法
     * @throws SecurityException 无访问权限抛出异常
     */
    public static Method getMethodOfObj(Object obj, String methodName, Object... args) throws SecurityException {
        if (null == obj || StrUtil.isBlank(methodName)) {
            return null;
        }
        return getMethod(obj.getClass(), methodName, ClassUtil.getClasses(args));
    }
    
    /**
     * 查找指定方法 如果找不到对应的方法则返回{@code null}
     *
     * <p>
     * 此方法为精准获取方法名，即方法名和参数数量和类型必须一致，否则返回{@code null}。
     * </p>
     *
     * @param clazz      类，如果为{@code null}返回{@code null}
     * @param methodName 方法名，如果为空字符串返回{@code null}
     * @param paramTypes 参数类型，指定参数类型如果是方法的子类也算
     * @return 方法
     * @throws SecurityException 无权访问抛出异常
     */
    @Nullable
    public static Method getMethod(Class<?> clazz, String methodName, Class<?>... paramTypes) throws SecurityException {
        return getMethod(clazz, false, methodName, paramTypes);
    }
    
    /**
     * 查找指定方法 如果找不到对应的方法则返回{@code null}<br>
     * 此方法为精准获取方法名，即方法名和参数数量和类型必须一致，否则返回{@code null}。<br>
     * 如果查找的方法有多个同参数类型重载，查找第一个找到的方法
     *
     * @param clazz      类，如果为{@code null}返回{@code null}
     * @param ignoreCase 是否忽略大小写
     * @param methodName 方法名，如果为空字符串返回{@code null}
     * @param paramTypes 参数类型，指定参数类型如果是方法的子类也算
     * @return 方法
     * @throws SecurityException 无权访问抛出异常
     */
    @Nullable
    public static Method getMethod(Class<?> clazz, boolean ignoreCase, String methodName, Class<?>... paramTypes) throws SecurityException {
        if (null == clazz || StrUtil.isBlank(methodName)) {
            return null;
        }
        
        final Method[] methods = getMethods(clazz);
        if (ArrayUtil.isNotEmpty(methods)) {
            for (Method method : methods) {
                if (ignoreCase ? methodName.equalsIgnoreCase(method.getName()) : methodName.equals(method.getName())
                        && ClassUtil.isAllAssignableFrom(method.getParameterTypes(), paramTypes)
                        && !method.isBridge()) {
                    return method;
                }
            }
        }
        return null;
    }
    
    /**
     * 获取方法的唯一键，结构为:
     * <pre>
     *     方法名:参数1类型,参数2类型...
     * </pre>
     *
     * @param method 方法
     * @return 方法唯一键
     */
    private static @NotNull String getUniqueKey(@NotNull Method method) {
        return method.getName() +
                "(" +
                Arrays.stream(method.getParameterTypes()).map(Class::getCanonicalName).collect(Collectors.joining(", ")) +
                ")";
    }
    
    
    /**
     * 获取方法描述
     * <pre>
     *     声明的类名 方法名(参数1类型,参数2类型...)
     * </pre>
     *
     * @param method 方法
     * @return 方法唯一键
     */
    private static @NotNull String getMethodDescription(@NotNull Method method) {
        return method.getDeclaringClass().getCanonicalName() +
                " " +
                method.getName() +
                "(" +
                Arrays.stream(method.getParameterTypes()).map(Class::getCanonicalName).collect(Collectors.joining(", ")) +
                ")";
    }
    
    /**
     * 获取类对应接口中的非抽象方法（default方法）
     *
     * @param clazz 类
     * @return 方法列表
     */
    private static @NotNull List<Method> getDefaultMethodsFromInterface(@NotNull Class<?> clazz) {
        List<Method> result = new ArrayList<>();
        for (Class<?> ifc : clazz.getInterfaces()) {
            for (Method m : ifc.getMethods()) {
                if (!ModifierUtil.isAbstract(m)) {
                    result.add(m);
                }
            }
        }
        return result;
    }
    
    /**
     * 当调用一个静态的{@link Method}时，目标对象可以是{@code null}。
     *
     * @see #invokeMethod(java.lang.reflect.Method, Object, Object[])
     */
    @Nullable
    public static Object invokeMethod(Method method, @Nullable Object target) {
        return invokeMethod(method, target, EMPTY_OBJECT_ARRAY);
    }
    
    /**
     * 当调用一个静态的{@link Method}时，目标对象可以是{@code null}。
     */
    @Nullable
    public static Object invokeMethod(@NotNull Method method, @Nullable Object target, @Nullable Object... args) {
        
        // 方法设置为可访问
        if ((!Modifier.isPublic(method.getModifiers()) ||
                !Modifier.isPublic(method.getDeclaringClass().getModifiers())) && !method.canAccess(target)) {
            method.setAccessible(true);
        }
        
        try {
            return method.invoke(target, args);
        } catch (Exception ex) {
            throw new UndeclaredThrowableException(ex);
        }
    }
    
    /**
     * 执行静态方法
     *
     * @param method 方法（对象方法或static方法都可）
     * @param args   参数对象
     * @return 结果
     */
    public static Object invokeStatic(Method method, Object... args) {
        return invokeMethod(method, null, args);
    }
    
    /**
     * 检查该方法是否被指定类中的方法重写
     *
     * @param method 可能被重写的方法
     * @param clazz  指定的类
     * @return 如果该方法被任何候选项覆盖则返回 true 反之为 false
     */
    public static boolean isOverridden(@NotNull Method method, @NotNull Class<?> clazz) {
        return isOverridden(method, getMethodsIncludeOverridden(clazz));
    }
    
    /**
     * 检查该方法是否被候选项列表中的任何方法重写
     *
     * @param method     可能被重写的方法
     * @param candidates 可能覆盖第一种方法的方法列表。这些方法应与第一种方法（包括继承的方法）来自同一类
     * @return 如果该方法被任何候选项覆盖则返回 true 反之为 false
     */
    public static boolean isOverridden(@NotNull Method method, @NotNull Method @NotNull [] candidates) {
        for (Method candidate : candidates) {
            if (isOverridden(method, candidate)) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * 检查方法是否已被候选项覆盖
     *
     * <p>覆盖规则：</p>
     * <ul>
     *   <li>候选人必须位于带方法的类的子类中</li>
     *   <li>方法名称必须相同</li>
     *   <li>参数必须相同。</li>
     *   <li>返回类型必须相同或子类</li>
     *   <li>访问修饰符必须相同或限制较少</li>
     *   <li>方法不能是私有的、最终的或静态的</li>
     *   <li>候选人不能是私有或静态的</li>
     *   <li>候选项必须与具有默认访问权限的方法位于同一包中。</li>
     * </ul>
     *
     * @return 如果该方法被任何候选项覆盖则返回 true 反之为 false
     */
    public static boolean isOverridden(@NotNull Method method, @NotNull Method candidate) {
        return method != candidate
                && hasOverridableAccessModifiers(method, candidate)
                && isSubClassOf(method.getDeclaringClass(), candidate.getDeclaringClass())
                && hasSameName(method, candidate)
                && hasSameParameters(method, candidate);
    }
    
    /**
     * 检查该方法是否具有与候选项相同的参数数、相同的参数顺序和完全相同的参数类型
     */
    public static boolean hasSameParameters(@NotNull Method method, @NotNull Method candidate) {
        final Class<?>[] methodParameters = method.getParameterTypes();
        final Class<?>[] candidateParameters = candidate.getParameterTypes();
        
        if (methodParameters.length != candidateParameters.length) {
            return false;
        }
        
        for (int i = 0; i < methodParameters.length; i++) {
            final Class<?> methodParameter = methodParameters[i];
            final Class<?> candidateParameter = candidateParameters[i];
            
            if (!methodParameter.equals(candidateParameter)) {
                return false;
            }
        }
        
        return true;
    }
    
    /**
     * 检查方法是否与候选项同名
     */
    public static boolean hasSameName(@NotNull Method method, @NotNull Method candidate) {
        return method.getName().equals(candidate.getName());
    }
    
    /**
     * 检查子类是否是超类的子类之一
     *
     * @param potentialSubclass   将检查以确定它是否是潜在超类的子类的类
     * @param potentialSuperclass 将检查潜在子类的类
     */
    public static boolean isSubClassOf(@NotNull Class<?> potentialSubclass, @NotNull Class<?> potentialSuperclass) {
        if (potentialSubclass.getSuperclass() != null) {
            if (potentialSubclass.getSuperclass().equals(potentialSuperclass)) {
                return true;
            }
            
            return isSubClassOf(potentialSubclass.getSuperclass(), potentialSuperclass);
        }
        
        return false;
    }
    
    /**
     * 根据方法的修饰符检查该方法是否可以由候选项重写
     * <p>
     * 私有、静态或最终方法不能被覆盖。仅当两个方法都来自同一包中的类时，才能重写包私有（默认）方法
     */
    public static boolean hasOverridableAccessModifiers(@NotNull Method method, @NotNull Method candidate) {
        if (isFinal(method) || isPrivate(method) || isStatic(method)
                || isPrivate(candidate) || isStatic(candidate)) {
            return false;
        }
        
        if (isPackagePrivate(method)) {
            return isSamePackage(method, candidate);
        }
        
        return true;
    }
    
    /**
     * 检查该方法是否是与候选项的类位于同一包中的类。
     */
    public static boolean isSamePackage(@NotNull Method method, @NotNull Method candidate) {
        Package methodPackage = method.getDeclaringClass().getPackage();
        Package candidatePackage = candidate.getDeclaringClass().getPackage();
        return methodPackage.equals(candidatePackage);
    }
    
    /**
     * 确定给定的方法是否为 equals 方法
     *
     * @see java.lang.Object#equals(Object)
     */
    public static boolean isEqualsMethod(@Nullable Method method) {
        if (method == null) {
            return false;
        }
        if (method.getParameterCount() != 1) {
            return false;
        }
        if (!method.getName().equals("equals")) {
            return false;
        }
        return method.getParameterTypes()[0] == Object.class;
    }
    
    /**
     * 确定给定的方法是否为 hashCode 方法
     *
     * @see java.lang.Object#hashCode()
     */
    public static boolean isHashCodeMethod(@Nullable Method method) {
        return method != null && method.getParameterCount() == 0 && method.getName().equals("hashCode");
    }
    
    /**
     * 确定给定的方法是否为 toString 方法
     *
     * @see java.lang.Object#toString()
     */
    public static boolean isToStringMethod(@Nullable Method method) {
        return (method != null && method.getParameterCount() == 0 && method.getName().equals("toString"));
    }
    
    /**
     * 检查成员是否为静态成员
     */
    public static boolean isStatic(@NotNull Member member) {
        return Modifier.isStatic(member.getModifiers());
    }
    
    /**
     * 检查成员是否为最终成员
     */
    public static boolean isFinal(@NotNull Member member) {
        return Modifier.isFinal(member.getModifiers());
    }
    
    /**
     * 检查成员是否为私有成员
     */
    public static boolean isPrivate(@NotNull Member member) {
        return Modifier.isPrivate(member.getModifiers());
    }
    
    /**
     * 检查成员是否为保护成员
     */
    public static boolean isProtected(@NotNull Member member) {
        return Modifier.isProtected(member.getModifiers());
    }
    
    /**
     * 检查成员是否为公共成员
     */
    public static boolean isPublic(@NotNull Member member) {
        return Modifier.isPublic(member.getModifiers());
    }
    
    /**
     * 检查成员是否为包专用。也称为默认修饰符，因为不存在修饰符。
     */
    public static boolean isPackagePrivate(final Member member) {
        return !isPublic(member) && !isProtected(member) && !isPrivate(member);
    }
    
}
